#ifndef BVD3D10RENDERER_H_
#define BVD3D10RENDERER_H_


#include <D3D10.h>
#include <d3dx10.h>
#include <vector>
#include "Platform.h"

#define MAX_MRTS 8
#define MAX_VERTEXSTREAM 8
#define MAX_TEXTUREUNIT  16
#define MAX_SAMPLERSTATE 16

#define TEXTURE_NONE  (-1)
#define SHADER_NONE   (-1)
#define BLENDING_NONE (-1)
#define VF_NONE   (-1)
#define VB_NONE   (-1)
#define TC_NONE   (-1)
#define EF_NONE   (-1)
#define IB_NONE   (-1)
#define SS_NONE   (-1)
#define BS_NONE   (-1)
#define DS_NONE   (-1)
#define RS_NONE   (-1)
#define FONT_NONE (-1)

#define FB_COLOR (-2)
#define FB_DEPTH (-2)
#define NO_SLICE (-1)

#define DONTCARE (-2)

//Utilities
#ifndef SAFE_RELEASE
#define SAFE_RELEASE(p)      { if (p) { (p)->Release(); (p)=NULL; } }
#endif

#define COLORMAP "ColorMap"

typedef int TextureId;
typedef int TechniqueId;
typedef int VertexBufferId;
typedef int IndexBufferId;
typedef int VertexFormatId;
typedef int SamplerStateId;
typedef int BlendStateId;
typedef int DepthStateId;
typedef int RasterizerStateId;
typedef int FontId;
typedef int SubMeshId;
typedef int EffectId;

struct Texture;
struct Technique;
struct VertexBuffer;
struct IndexBuffer;
struct VertexFormat;
struct SamplerState;
struct BlendState;
struct DepthState;
struct RasterizerState;
struct Effect;

enum BufferAccess {
    STATIC,
    DEFAULT,
    DYNAMIC,
};

enum Primitives {
    PRIM_TRIANGLES      = 0,
    PRIM_TRIANGLE_FAN   = 1,
    PRIM_TRIANGLE_STRIP = 2,
    PRIM_QUADS          = 3,
    PRIM_LINES          = 4,
    PRIM_LINE_STRIP     = 5,
    PRIM_LINE_LOOP      = 6,
    PRIM_POINTS         = 7,
};

enum AttributeType {
    TYPE_GENERIC  = 0,
    TYPE_VERTEX   = 1,
    TYPE_TEXCOORD = 2,
    TYPE_NORMAL   = 3,
    TYPE_TANGENT  = 4,
    TYPE_BINORMAL = 5,
    TYPE_COLOR    = 6,
};

enum AttributeFormat {
    FORMAT_FLOAT = 0,
    FORMAT_HALF  = 1,
    FORMAT_UBYTE = 2,
};

enum EffectVariableType {
    EFFECT_VARIABLE_UNKNOWN = 0,
    EFFECT_VARIABLE_SCALAR_BOOL = 1,
    EFFECT_VARIABLE_SCALAR_BOOLARRAY = 2,
    EFFECT_VARIABLE_SCALAR_FLOAT = 3,
    EFFECT_VARIABLE_SCALAR_FLOATARRAY = 4,
    EFFECT_VARIABLE_SCALAR_INT = 5, 
    EFFECT_VARIABLE_SCALAR_INTARRAY = 6,
    EFFECT_VARIABLE_VECTOR_BOOL = 7,
    EFFECT_VARIABLE_VECTOR_BOOLARRAY = 8,
    EFFECT_VARIABLE_VECTOR_INT = 9,
    EFFECT_VARIABLE_VECTOR_INTARRAY = 10,
    EFFECT_VARIABLE_VECTOR_FLOAT = 11,
    EFFECT_VARIABLE_VECTOR_FLOATARRAY = 12,
    EFFECT_VARIABLE_MATRIX = 13,
    EFFECT_VARIABLE_MATRIXARRAY = 14,
    EFFECT_VARIABLE_SHADERRESOURCE = 15,
};

struct EffectVariable {
    EffectVariableType type;
    ID3D10EffectVariable* d3d10EffectVariable;
    char* varName;
    bool isDirty;
};

struct FormatDesc {
    //TODO: stream is always 0, how to improve it?
    int stream;
    AttributeType type;
    AttributeFormat format;
    int size;
};


struct SubMesh {
    VertexBufferId vbId;
    IndexBufferId ibId;
    EffectId eftId;
    char* techName;
    VertexFormatId vfId;
};

const D3D10_PRIMITIVE_TOPOLOGY d3dPrim[] = {
    D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
    D3D10_PRIMITIVE_TOPOLOGY_UNDEFINED, // Triangle fans not supported
    D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
    D3D10_PRIMITIVE_TOPOLOGY_UNDEFINED, // Quads not supported
    D3D10_PRIMITIVE_TOPOLOGY_LINELIST,
    D3D10_PRIMITIVE_TOPOLOGY_LINESTRIP,
    D3D10_PRIMITIVE_TOPOLOGY_UNDEFINED, // Line loops not supported
    D3D10_PRIMITIVE_TOPOLOGY_POINTLIST,
};


class BvD3D10Renderer
{
public:
    BvD3D10Renderer(ID3D10Device* d3ddev);
    ~BvD3D10Renderer();

    VertexBufferId BvD3D10Renderer::addVertexBuffer(
        const void *data, const uint size, const BufferAccess 
        bufferAccess = DEFAULT, ID3D10Buffer** buffer = NULL);

    void setVertexBuffer(
        const int stream, const VertexFormatId vfId,
        const VertexBufferId vbId, const uint offset);

    IndexBufferId addIndexBuffer(
        const void *data, const uint singleIndexSize, const uint nIndices, 
        const BufferAccess bufferAccess = DEFAULT );

    void setIndexBuffer(IndexBufferId ibId);

    EffectId addEffect(const char* shaderPath);

    void addEffectTechniques(const char* shaderPath);

    VertexFormatId BvD3D10Renderer::addEffectVertexFormat(
        const FormatDesc *formatDesc, const uint nAttribs, EffectId eftId,
        const char* techName);

    VertexFormatId addMeshVertexFormat(
        const FormatDesc *formatDesc, const uint nAttribs, SubMesh& sm);

    VertexFormatId addVertexFormat(
        const FormatDesc *formatDesc, const uint nAttribs, 
        D3D10_PASS_DESC* pPassDesc);

    void setVertexFormat(VertexFormatId vfId);

    SubMeshId addSubMesh(SubMesh sb);

    HRESULT GetTechniquePassByIndexDesc(D3D10_PASS_DESC* pPassDesc,
        const int eftId, const char* techName, const int indexId = 0);

    void drawVertexBuffer(const Primitives primitives, 
        const int nVertices, const int firstVertex);
    
    void drawIndexBuffer(const Primitives primitives, 
        const int nIndices, const int firstIndex);

    void drawMeshes(const Primitives primitives, 
        const int nIndices, const int firstIndex = 0,
        const int nVertices = 0, const int firstVertex = 0);

    void drawSubMesh(const SubMeshId smId, const Primitives primitives, 
        const int nIndices, const int firstIndex = 0,
        const int nVertices = 0, const int firstVertex = 0);

    EffectVariable* getEffectVariableByName(
        const EffectId eftId, const char* varName, 
        const EffectVariableType type);

    void setEffectVariable(
        const EffectVariable* pEffectVariable, float* pData);

    //another way to update the variable in shader
    //replace getEffectVariableByName and setEffectVariable
    bool setEffectVariableByName(
        const EffectId eftId, const char* varName, 
        const EffectVariableType type, void* pData, 
        const int offset = 0, const uint nData = 0);

    ID3D10ShaderResourceView* createSRVFromFile(
        const char* fileName, D3DX10_IMAGE_LOAD_INFO* pLoadInfo = NULL);

protected:

    ID3D10Device* device;
    //uint d_bufferSize;

    std::vector<VertexBuffer> vertexBufferList;
    std::vector<IndexBuffer> indexBufferList;
    std::vector<VertexFormat> vertexformatList;
    std::vector<SubMesh> subMeshList;
    std::vector<Effect> effectList;

    //EffectId currentEffectId;

public:
    //TODO: no use
    ID3D10Effect* GetEffectById(EffectId eftId);

    int getFormatSize(const AttributeFormat format) const ;
};

#endif  //BVD3D10RENDERER_H_